(function () {
    importScripts('rbush.js');
    var _tree = rbush(16);  // 索引树

    var shouldRun = false;
    var isStillUpdating = true;
    var stack = [];
    var timers = {};
    var _maxCountOfVisibleTiles;
    var _bbox, _rownum, _colnum, _rowDelta, _colDelta;

    var listeners = {
        initWorker: function (frame, maxCountOfVisibleTiles) {
            shouldRun = true;
            _maxCountOfVisibleTiles = maxCountOfVisibleTiles;
            listeners['checkDataPool'].apply(self, [frame]);
        },

        checkDataPool: function (frame) {
            if (!shouldRun) return;

            if (frame == null) {
                stack = [];
                listeners['updateTaskStack'].apply(self);
                return;
            }

            var dataPool = listeners["queryByMatrix"].apply(self, [frame]);
            // console.log('dataPool.length: ' + dataPool.length);
            if (dataPool.length === 0) {
                stack = [];
                listeners['updateTaskStack'].apply(self);
                return;
            }

            // Tiles within the view frame are sorted to make sure that the tiles near to the camera will be loaded with higher priority
            // 对视图框内的图块进行排序，以确保将以更高的优先级加载相机附近的图块
            stack = listeners["sortDatapool"].apply(self, [dataPool, frame]);
            // console.log(stack);
            if (stack.length > 100) {
                stack = stack.splice(0, 100);
            }
            var matrixItem = stack.shift();
            reply("checkMasterPool", matrixItem, stack);
        },

        sortDatapool: function (pool, frame) {
            var centerX = frame.refX;
            var centerY = frame.refY;
            pool.sort(function (a, b) {
                var ax = a.x;
                var ay = a.y;
                var as = Math.sqrt((centerX - ax) * (centerX - ax) + (centerY - ay) * (centerY - ay));

                var bx = b.x;
                var by = b.y;
                var bs = Math.sqrt((centerX - bx) * (centerX - bx) + (centerY - by) * (centerY - by));

                return as - bs;
            });
            return pool;
        },

        updateDataPoolRecord: function () {
            isStillUpdating = true;
        },

        notifySleep: function () {
            shouldRun = false;
        },

        notifyWake: function () {
            shouldRun = true;
            reply("removeDatasources");
        },

        abortAndnotifyWake: function () {
            for (var id in timers) {
                if (timers.hasOwnProperty(id)) {
                    clearTimeout(timers[id]);
                }
            }
            stack = [];
            listeners["notifyWake"].apply(self);
        },

        createMatrix: function (bbox, rowDelta, colDelta, rownum, colnum) {
            _bbox = bbox;
            _rowDelta = rowDelta;
            _colDelta = colDelta;
            _rownum = rownum;
            _colnum = colnum;
        },

        createRTree: function (data) {
            _tree.load(data);
        },

        queryByMatrix: function (frame) {
            // console.time('treeQuery');
            var dataPool = _tree.search({
                minX: frame.minX,
                minY: frame.minY,
                maxX: frame.maxX,
                maxY: frame.maxY
            });
            // console.timeEnd('treeQuery');

            return dataPool;
        },

        checkFrameBbox: function (funcName, frame) {
            var frameMinX = frame.minX;
            var frameMinY = frame.minY;
            var frameMaxX = frame.maxX;
            var frameMaxY = frame.maxY;
            var minCol = Math.floor((frameMinX - _bbox.xmin) / _colDelta);
            var maxCol = Math.floor((frameMaxX - _bbox.xmin) / _colDelta);
            var minRow = Math.floor((frameMinY - _bbox.ymin) / _rowDelta);
            var maxRow = Math.floor((frameMaxY - _bbox.ymin) / _rowDelta);
            var numberOfTiles = (maxCol - minCol + 1) * (maxRow - minRow + 1);
            if (numberOfTiles <= _maxCountOfVisibleTiles) {
                reply(funcName, true);
            } else {
                reply(funcName, false);
            }
        },

        pushTaskItem: function (item) {
            stack.push(item);
            listeners["updateTaskStack"].apply(self);
        },

        generateUUID: function () {
            var d = new Date().getTime();
            var uuid = 'xxxxxxxx_xxxx_4xxx_yxxx_xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                var r = (d + Math.random() * 16) % 16 | 0;
                d = Math.floor(d / 16);
                return (c === 'x' ? r : (r & 0x7 | 0x8)).toString(16);
            });
            uuid = "UUID_" + uuid;
            return uuid;
        },

        updateTaskStack: function (pauseTime) {
            var _time = 10 + 30 * Math.random();
            if (typeof pauseTime != 'undefined') {
                _time = pauseTime;
            }
            var uuid = listeners["generateUUID"].apply(self);
            var timer = setTimeout(function () {
                delete timers[uuid];
                var matrixItem = stack.shift();
                if (typeof matrixItem == 'undefined') {
                    if (isStillUpdating) {
                        isStillUpdating = false;
                        reply("removeDatasources");
                    } else {
                        if (shouldRun) {
                            shouldRun = false;
                            reply("refreshView");
                        }
                    }
                } else {
                    reply("checkMasterPool", matrixItem, stack);
                }
            }, _time);
            timers[uuid] = timer;
        }
    };

    //======================= system functions ============================//

    function reply() {
        if (arguments.length < 1) {
            throw new TypeError("Not enough arguments");
        }
        postMessage({
            "vo42t30": arguments[0],
            "rnb93qh": Array.prototype.slice.call(arguments, 1)
        });
    }

    function defaultListener(message) {
        // do nothing
    }

    self.onmessage = function (e) {
        if (e.data instanceof Object && e.data.hasOwnProperty('bk4e1h0') && e.data.hasOwnProperty('ktp3fm1')) {
            listeners[e.data.bk4e1h0].apply(self, e.data.ktp3fm1);
        } else {
            defaultListener(e.data);
        }
    };
})();